<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Closure -- Soya 1.0.1-alpha</title>
    <link type="text/css" rel="stylesheet" href="../css/style.css">
</head>
<body>
    <div class="background">
        <div>
            <h2>闭包</h2>
        </div>
        <div>
            闭包是函数式编程语言中的一个概念，能够将函数像数据一样操作。Soya提供了闭包定义和执行的语法，创建好的闭包可以作为一个对象存在变量中，也可以在方法的参数中传递。在Soya中可以使用 # 加上代码块的形式定义闭包，闭包的调用和普通方法的调用没什么两样，但不能省略括号。
        </div>
        <div class="code">
            <pre>
foo := #{println('ok')}
foo()</pre>
        </div>
        <div>
            <h3>参数</h3>
        </div>
        <div>
            可以像定义普通方法参数列表一样定义闭包的参数，但要在开头打上 # 符号
        </div>
        <div class="code">
            <pre>
sum := #(a, b) {a + b}
println(sum(2, 3))  // --> 5</pre>
        </div>
        <div>
            <h3>变量</h3>
        </div>
        <div>
            除了在闭包自身的定义的参数和变量外，可以随意的引用外部定义的变量、属性和方法
        </div>
        <div class="code">
            <pre>
num := 7
plusNum := #(x) {x + num}
println(plusNum(5))  // --> 12</pre>
        </div>
        <div>
            在Soya的闭包中提供了一些内置变量可供您来引用
        </div>
        <div>
            <h3>it -- 如果在调用闭包时输入若干参数，it则引用输入的第一个参数</h3>
        </div>
        <div class="code">
            <pre>
twice := #{it * 2}
println(twice(6))  // --> 12</pre>
        </div>
        <div>
            <h3>this -- 就像在其它java方法中一样，this引用的是闭包定义所在外层class的实例</h3>
        </div>
        <div class="code">
            <pre>
class ClassA

def doSomething() {
    println('do someting')
}

def doA() {
    println('begin do A')
    foo := {
        this.doSomething()
    }
    foo()
    println('end do A')
}

static def main(args) {
    new ClassA().doA()
}
// -->
// begin do A
// do something
// end do A</pre>
        </div>
        <div>
            <h3>传入方法参数</h3>
        </div>
        <div>
            当闭包类对象作为最后一个参数传入调用方法时，允许用在调用语句后定义代码块的方式
        </div>
        <div class="code">
            <pre>
list := ['x', 'y', 1, 2, 4, 5]
list.each(String, Index i) {
    println("{i}: {it}")
}
// -->
// 0: x
// 1: y</pre>
        </div>
        <div>
            不过这种方式不允许定义参数，只能透过it变量和模式参数的别名获取传入的数据。若想要传入方法参数的闭包定义参数，或调用方法时闭包不在其参数列表中的最后一个位置，则需要像下面那样：
        </div>
        <div class="code">
            <pre>
list := [1, 3, 2, 5]
list.sort(#(a, b) {a > b})</pre>
        </div>
        <div>
            <h3>引用对象方法</h3>
        </div>
        <div>
            闭包的另一种形式是引用对象方法，类似于函数指针。并能将引用对象存在变量中或传入调用方法参数
        </div>
        <div class="code">
            <pre>
def foo(x) {
    println("do {x}")
}

ref := #foo
ref('what')
// --> do what

list := [1, 2, 3]
lref := list.#toString
println(lref())
// --> [1, 2, 3]</pre>
        </div>
        <div>
            在执行引用方法时，若被引用的方法有个多重载版本，则根据传入参数数目不同选择对应的方法执行
        </div>
        <div class="code">
            <pre>
def foo() {
    println("do foo")
}

def foo(a) {
    println("do foo {a}")
}

def foo(a, b) {
    println("do foo {a}, {b}")
}

ref := #foo
ref('x', 'y')
ref()
ref(100)
// -->
// do foo x, y
// do foo
// do foo 100</pre>
        </div>
    </div>
</body>
</html>